Skip to content

feat: 알림 조회·읽음 처리 API (GET/POST notifications) - #123

Draft
chaeliki wants to merge 28 commits into
mainfrom
feat/15-notifications
Draft

feat: 알림 조회·읽음 처리 API (GET/POST notifications)#123
chaeliki wants to merge 28 commits into
mainfrom
feat/15-notifications

Conversation

@chaeliki

@chaeliki chaeliki commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

왜 필요한가요?

Refs #15

PR #115, #119 위에 이어서, Notification 도메인 전체(테이블, 조회 API,
읽음 처리 API)를 추가합니다. 알림을 실제로 생성하는 로직(이벤트 기반)은
후속 PR로 분리했습니다.

완료된 범위

  • V37: notification 테이블
  • V38: notification RLS 정책
  • Notification 도메인 (markAsRead()가 멱등하게 설계됨)
  • NotificationRepository (cursor 기반 페이지네이션)
  • GET /api/v1/notifications (unreadOnly, cursor, size 파라미터)
  • POST /api/v1/notifications/{notificationId}/read
  • recommendations의 review/after_approval을 dueDate 우선 정렬 (feat: today 대시보드 Agent 추천 요약 #119 )

결정 사항

  • markAsRead()는 이미 읽은 상태면 자기 자신을 그대로 반환하도록 만들어,
    불필요한 DB 쓰기 없이 멱등성을 보장합니다.
  • 아직 알림을 생성하는 로직이 없어-> 이벤트 생성을 의미, 통합 테스트는 DB에 직접 INSERT해
    테스트 데이터를 준비했습니다. 실제 생성 로직 검증은 후속 PR에서 다룹니다.

어떻게 검증했나요?

  • ./gradlew clean test - 전체통과
  • listReturnsItemsAndUnreadCount - 알림 2건(읽음 1, 안읽음 1) 준비 후
    목록 조회 시 items 2건과 unread_count 1이 정확히 나오는지 확인
  • unreadOnlyFiltersReadNotifications - unreadOnly=true로 조회하면
    읽은 알림이 제외되고 안읽은 것만 나오는지 확인
  • otherCompanyNotificationsAreNotVisible - 다른 사업장의 알림이
    내 목록 조회에 전혀 안 나타나는지 확인 (tenant 격리)
  • readMarksNotificationAsReadAndIsIdempotent - 같은 알림에 읽음 처리를
    두 번 연속 호출해도 둘 다 204가 나오고, 목록에서 실제로 read=true로
    반영되는지 확인 (멱등성)
  • readOnOtherCompanyNotificationReturnsNotFound - 다른 사업장의
    알림을 읽음 처리하려고 하면 404가 나오는지 확인 (존재 여부도 노출 안 함)
  • NotificationTest (도메인 단위 테스트) - buildRoute()가 TASK/WORKER/
    DOCUMENT 각각 정확한 경로를 생성하는지, route가 항상 target_id를
    포함하는지, markAsRead()가 멱등한지 확인
  • notificationsAreIsolatedBetweenUsersInSameCompany - 같은 회사
    내 다른 사용자(HR_A2)에게는 HR_A의 알림이 전혀 보이지 않는지 확인
  • hasNextIsFalseWhenExactlySizeItemsRemain — 정확히 요청한 개수(3개)
    만큼만 남아있을 때, has_next가 false로 정확히 나오는지 확인
    (+1 전략 이전에는 이 경계에서 잘못 true가 나올 수 있었음)
  • hasNextIsTrueWhenMoreItemsRemain - 요청한 개수보다 많이 남아있을
    때, has_next가 true로 정확히 나오고 items는 요청한 개수만큼만
    잘리는지 확인

리뷰 반영

  • user_id 추가: notification 테이블에 user_id 컬럼을 추가하여,
    같은 회사 내 다른 사용자에게는 읽음 처리가 전파되지 않도록 수정했습니다.
  • route 자동 생성: target_id와 route를 각각 입력받던 방식에서,
    target_type + target_id로 서버가 route를 직접 생성하도록
    변경했습니다(Notification.buildRoute()). target_id와 route가
    서로 다른 값을 가리키는 불일치가 구조적으로 불가능해집니다.

추가 개선

  • cursor 페이지네이션에 +1 전략을 적용해, has_next 필드가 경계값에서도
    정확하도록 수정했습니다.

chaeliki and others added 25 commits August 8, 2026 01:51
# Conflicts:
#	src/main/java/com/fowoco/server/dashboard/application/DashboardQueryService.java
@chaeliki
chaeliki requested a review from hywznn August 9, 2026 01:33
@chaeliki
chaeliki marked this pull request as draft August 9, 2026 02:00
@chaeliki
chaeliki force-pushed the feat/15-notifications branch from 5f70f5b to f81371b Compare August 9, 2026 02:27
@hywznn

hywznn commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

한 사람이 읽으면 회사의 모든 사람에게 읽음 처리됨
현재 알림에는 company_id만 있고, 알림을 받을 user_id가 없습니다
예를 들어 같은 회사의 A 직원이 알림을 읽으면 B 직원의 화면에서도 읽은 알림이 될 것 같습니다
위치: V37__create_notification.sql

수정 방법:
알림 테이블에 recipient_user_id 추가
목록, 안 읽은 개수, 읽음 처리를 모두 company_id + recipient_user_id로 조회
같은 회사 사용자 A가 읽어도 B에게는 안 읽은 상태로 남는 테스트 추가

==

알림을 눌렀을 때 잘못된 화면으로 갈 수 있음
현재 target_id와 route를 따로 받아서 저장합니다.
두 값이 서로 달라도 검사하지 않습니다. 실제 테스트 데이터도 target_id와 주소에 들어간 Task ID가 다릅니다

서버가 target_type과 target_id를 기준으로 주소를 직접 만들면 됩니다
TASK → /tasks/{targetId}
WORKER → /workers/{targetId}
DOCUMENT → /documents/{targetId}

참고로 이 PR에는 알림을 실제로 생성하는 기능은 없습니다. 따라서 이 PR만 머지하면 알림 목록은 비어 있고, 후속 생성 PR까지 적용돼야 화면에 알림이 나타납니다.

=> 빠진 것은 POST API가 아니라, 업무 이벤트가 발생했을 때 실제 알림 데이터를 생성하는 기능입니다. 사용자나 프론트가 알림을 직접 생성하는 POST API는 보통 필요하지 않습니다.

실제 알림 생성은 후속PR로 미루는게 어떠신지요

@chaeliki

chaeliki commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

음 pr 본문에 설명이 부족햇던것 같은데 애초에 따로 할려고 먼저 올린거고, 그래서 이미 실제 후속 pr에서 이벤트 발생을 다루고 있습니다. 제가 설명이 부족했네요 생성쪽은 api 관련이 아니라 미리 pr 나눠서 진행할려 했던거고 각자 검증 해야하는 테스트 영역이 미묘하게 달라서 오히려 더 좋다고 생각했습니다, 또한 user id 부분 또한 현재 수정중인 사항과 같이 수정 커밋 푸쉬 되었지만 오류 발견으로 다시 되돌렸습니다. 더 신경써서 리뷰 요청드리겠습니다

아 그리고 라우트 주소도 ~이부분도

먼저 123 pr 올리고 이벤트 생성쪽 처리중, 에이전트 초안작성완료와 만료 알림은 선행된 필터기능과 스케쥴 기능 등 기타 요소로 만들 수 있었는데, 그 외의 승인 요청 / 문서 보완 요청 / 문서 제출 완료등 (또한 어떤 기준으로 알림을 발송하는지도 모호함)기능들이 이벤트를 발생시키기에 뭔가 어려운 부분들이 존재하여 질문 남겼습니다

@hywznn

hywznn commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

음 pr 본문에 설명이 부족햇던것 같은데 애초에 따로 할려고 먼저 올린거고, 그래서 이미 실제 후속 pr에서 이벤트 발생을 다루고 있습니다. 제가 설명이 부족했네요 생성쪽은 api 관련이 아니라 미리 pr 나눠서 진행할려 했던거고 각자 검증 해야하는 테스트 영역이 미묘하게 달라서 오히려 더 좋다고 생각했습니다, 또한 user id 부분 또한 현재 수정중인 사항과 같이 수정 커밋 푸쉬 되었지만 오류 발견으로 다시 되돌렸습니다. 더 신경써서 리뷰 요청드리겠습니다

아 그리고 라우트 주소도 ~이부분도

먼저 123 pr 올리고 이벤트 생성쪽 처리중, 에이전트 초안작성완료와 만료 알림은 선행된 필터기능과 스케쥴 기능 등 기타 요소로 만들 수 있었는데, 그 외의 승인 요청 / 문서 보완 요청 / 문서 제출 완료등 (또한 어떤 기준으로 알림을 발송하는지도 모호함)기능들이 이벤트를 발생시키기에 뭔가 어려운 부분들이 존재하여 질문 남겼습니다

제가 아침에 급급해서 본문내용이랑 필요한 부분만 보고 말씀드린것 같군요..,.!

hywznn
hywznn previously approved these changes Aug 9, 2026

@hywznn hywznn left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

밖에 나와있어서 코드에 코멘트가 안달아지네요 도메인/노티피케이션 에 라우팅 추가한거 확인했습니다!

userid도 다르게한거 확인했습니다 !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants